


iMacros.jsplayer2 = (function() {
    const Cu = Components.utils;
    const Ci = Components.interfaces;
    const Cc = Components.classes;
    function require(url) {
        let scope = {};
        Cu.import(url, scope);
        return scope;
    }
    let {Prefs} = require("chrome://jsm/content/Preferences.js");
    let {StrUtils} = require("chrome://jsm/content/utils.js");
    let {addDebuggerToGlobal} =
        require("resource://gre/modules/jsdebugger.jsm");
    let {Services} = require("resource://gre/modules/Services.jsm");
    let {OS} = require("resource://gre/modules/osfile.jsm");

    function JS_Player() {
        let principal = Cc["@mozilla.org/systemprincipal;1"]
            .createInstance(Ci.nsIPrincipal);
        let global = Components.utils.Sandbox(
            principal, {wantComponents: true}
        );
        addDebuggerToGlobal(global);
    
        this.dbg = new global.Debugger();
        
        
        
        

        this.dbg.onNewScript = function(script, func) {
            
            iMacros.jsplayer2.startLine = script.startLine;
        };

        
        
        

        this.dbg.onEnterFrame = function(frame) {
            
            
            
            
            

            if (!iMacros.jsplayer2.visualize || !frame.live || !frame.script)
                return;

            let offsets = frame.script.getAllOffsets();
            for (let i = 0; i < offsets.length; i++) {
                if (offsets[i]) {
                    frame.script.setBreakpoint(
                        offsets[i][0],
                        iMacros.jsplayer2
                    )
                }
            }

            frame.onStep = function() {
                return iMacros.jsplayer2.onStep();
            };
        };

        
        
        

        this.dbg.uncaughtExceptionHook = function(ex) {
            
            Components.utils.reportError(ex);
            return undefined;
        };

        this.shouldProceed = false;
        this.delay = 0;
        this.visualize = false;
        this.stopIsPending = false;
        this.pauseIsPending = false;
        this.paused = false;
        this.playing = false;
    };


    
    JS_Player.prototype.hit = function(frame) {
        if (frame.script) {
            let line = frame.script
                .getOffsetLocation(frame.offset).lineNumber-this.startLine+1;
            iMacros.panel.highlightLine(line);
            
            
            var ct = Services.tm.currentThread;
            
            this.shouldProceed = false;
            setTimeout(function() {
                iMacros.jsplayer2.shouldProceed = true;
            }, this.delay);
            
            
            while(!this.shouldProceed)
                ct.processNextEvent(true);
            
        } else {
            Components.utils.reportError(
                "iMacros JS_Player, breakpoint handler: no frame.script!"
            );
        }
    };

    
    
    JS_Player.prototype.onStep = function() {
        if (this.pauseIsPending) {
            this.pauseIsPending = false;
            this.paused = true;
            if (this.pauseCallback) {
                this.pauseCallback();
                this.pauseCallback = null;
            }
            var ct = Services.tm.currentThread;
            
            while(this.paused && !this.stopIsPending)
                ct.processNextEvent(true);
        }

        if (this.stopIsPending) {
            this.stopIsPending = false;
            return null;
            
        }
    };

    JS_Player.prototype.play = function(code, name) {
        this.paused = false;
        
        
        
        
        
        
        
        let principal = Cc["@mozilla.org/systemprincipal;1"]
            .createInstance(Ci.nsIPrincipal);
        
        var full_access = true;
        let sandbox = Components.utils.Sandbox(
            principal, {wantComponents: full_access}
        );
        this.attachSIMethods(sandbox);
        this.attachWindowMethods(sandbox);
        this.dbg.enabled = true;
        this.dbg.addDebuggee(sandbox);
        this.playing = true;
        
        this.visualize = Prefs.visualizeJavascript;

        
        if (/^\/\/imacros-js:showsteps\s+(yes|no)\b/i.test(code))
            this.visualize = RegExp.$1 == "yes";

        if (this.visualize) {
            iMacros.panel.showLines(code);
            this.delay = Prefs.replayingDelay;
        } else {
            this.delay = 0;
        }
        iMacros.panel.statLine3 = name;
        iMacros.panel.mboxResetError();
        iMacros.panel.updateControlPanel();
        try {
            Components.utils.evalInSandbox(code, sandbox);
        } catch (e) {
            
            let errtext = e.toString();
            
            
            
            
            
            
            if (!/NS_ERROR_OUT_OF_MEMORY/.test(errtext)) {
                Components.utils.reportError(e);
                
                errtext += ", line "+(e.lineNumber - this.startLine + 1);
                iMacros.panel.showErrorMessage(errtext, -991);
                
                if (iMacros.client_id) {   
                    this.errorMessage = errtext;
                    this.errorCode = -991;
                }
            }
        } finally {
            this.dbg.clearAllBreakpoints();
            this.dbg.removeDebuggee(sandbox);
            this.dbg.enabled = false;
            this.paused = false;
            this.playing = false;
            iMacros.panel.showMacroTree();

            if (iMacros.client_id) {   
                if (!this.errorMessage) {
                    this.errorMessage = "OK";
                    this.errorCode = 1;
                }
                var sicmd = Cc["@iopus.com/sicmdlistener;1"].
                    getService(Ci.nsISupports).wrappedJSObject;
                sicmd.sendResponse(
                    iMacros.client_id,
                    this.errorMessage,
                    this.errorCode,
                    {
                        extractData: "",
                        lastPerformance: []
                    }
                );
                delete this.errorMessage;
                delete this.errorCode;
                delete iMacros.client_id;
            }
        }
    };


    JS_Player.prototype.stop = function() {
        this.stopIsPending = true;
    };

    JS_Player.prototype.pause = function(callback) {
        if (!this.paused) {
            this.pauseIsPending = true;
            this.pauseCallback = callback;
        }
    };

    JS_Player.prototype.unPause = function(callback) {
        if (!this.paused) {
            this.pauseIsPending = false;
            if (callback)
                callback();
        } else {
            this.paused = false;
            if (callback)
                callback();
        }
    };

    JS_Player.prototype.attachSIMethods = function(sandbox) {
        sandbox.iimPlay = function(macro_or_code) {
            var x = macro_or_code, macro = {};
            if (/^code:((?:\n|.)*)$/i.test(x)) {
                var src = RegExp.$1;
                src = src.replace(/\[sp\]/gi, ' ');
                src = src.replace(/\[lf\]/gi, '\r');
                src = src.replace(/\[br\]/gi, '\n');
                macro.source = src;
            } else {
                
                let path = OS.Path.join(
                    Prefs.defaultMacroPath,
                    ...OS.Path.split(OS.Path.normalize(x)).components
                );
                if (!/\.iim$/i.test(path))
                    path += ".iim";

                macro.path = path;
            }

            iMacros.in_iimPlay = true;
            iMacros.player.play(macro);
            
            
            var ct = Services.tm.currentThread;
            
            while(iMacros.player.playing)
                ct.processNextEvent(true);
            
            iMacros.in_iimPlay = false;
            iMacros.panel.showLines(iMacros.jssrc);

            return iMacros.player.errorCode;
        };

        sandbox.iimPlayCode = function(code) {
            iMacros.in_iimPlay = true;
            iMacros.player.play(code, 1, "Inline code");
            var ct = Services.tm.currentThread;
            while(iMacros.player.playing)
                ct.processNextEvent(true);
            
            iMacros.in_iimPlay = false;
            iMacros.panel.showLines(iMacros.jssrc);

            return iMacros.player.errorCode;
        };

        sandbox.iimDisplay = function(txt) {
            iMacros.panel.showInfoMessage(txt);
            return 1;
        };

        sandbox.iimExit = function() {
            iMacros.jsplayer2.stop();
        };

        sandbox.iimClose = function() {
            sandbox.iimExit();
        };

        sandbox.iimGetLastError = function() {
            return iMacros.player.errorMessage || "OK";
        };
        
        sandbox.iimGetErrorText = function() {
            return sandbox.iimGetLastError();
        };

        sandbox.iimGetLastPerformance = function() {
            throw "iimGetLastPerformance not supported!";
        };

        sandbox.iimSet = function (name, val) {
            try {
                
                val = val.toString();
                let m = name.match(/^(?:-var_)?(\w+)$/);
                if ( m )
                    name = m[1];

                m = name.match(/^!var([0-9])$/i);
                if (m) {
                    iMacros.player.vars[StrUtils.toInteger(m[1])] = val;
                } else {
                    iMacros.player.userVars.set(name.toLowerCase(), val);
                }
            } catch(e) {Cu.reportError(e);}
            return 1;
        };

        sandbox.iimGetLastExtract = function(val) {
            if ( !val )
                return iMacros.player.getExtractData();

            let h = iMacros.player.getExtractData();
            if (!h || !h.length)
                return null;
            val = StrUtils.toInteger(val);
            if (isNaN(val) || h.length < val-1)
                return "#nodata#";
            h = h.split("[EXTRACT]");

            return h[val-1];
        };

        sandbox.iimGetExtract = function(val) {
            return sandbox.iimGetLastExtract();
        };
        
    };


    JS_Player.prototype.attachWindowMethods = function(sandbox) {
        sandbox.prompt = function(msg, def_value) {
            return typeof def_value == "undefined"?
                prompt(msg): prompt(msg, def_value);
           
        };

        sandbox.alert = function(msg) {
            return alert(msg);
        };

        sandbox.confirm = function(msg) {
            return confirm(msg);
        };

        
        sandbox.__defineGetter__("window", function() {
            throw new Error("Accessing content window is not supported in "+
                           "multiprocess Firefox")
        });

        sandbox.__defineGetter__("content", function() {
            throw new Error("Accessing content window is not supported in "+
                           "multiprocess Firefox")
        });
    };
    
    JS_Player.prototype.isPaused = function() {
        return this.paused;
    };
    
    return new JS_Player();
}) ();

